<?PHP
/*
 * $Id: barge-web.classes.php 139 2009-12-16 15:29:56Z namba0219 $
 * =============================================================================
 * Barge - Web application framework on PHP5
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * =============================================================================
 * For more information, see http://barge.googlecode.com/
 */
namespace barge\web;

use \Exception;
use \ReflectionClass;

use barge\ClassLoader;
use barge\System;
use barge\logging\LogFactory;
use barge\io\File;
use barge\io\FileNotFoundException;
use barge\io\FileInputStream;
use barge\utils\ArrayList;
use barge\utils\Bean;
use barge\utils\BeanUtils;
use barge\utils\Config;
use barge\utils\ExceptionUtils;
use barge\utils\HashMap;
use barge\utils\IllegalAccessException;
use barge\utils\IllegalArgumentException;
use barge\utils\String;
use barge\utils\Selector;
use barge\utils\TimeValue;
use barge\utils\UndefinedConfigException;
use barge\utils\LocalString;
use barge\utils\URLUtils;
use barge\utils\XMLConfig;


/**
 * Custom exception.
 *
 * @author Kenji Namba
 * @version 1.0
 * @since barge 1.0 2006/08/12
 */
class UnauthorizedException extends Exception {
}

/**
 * @version 1.0 2008/09/08
 * @since Barge 1.2 2008/09/08
 */
interface ModuleLoader
{
	/**
	 * モジュールのロード処理が行われた際に1回のみ呼ばれます
	 * @param ModuleManager $manager
	 * @return bool
	 */
	public function load(ModuleManager $manager);

	/**
	 * すべてのモジュールのロードが完了した後に1回だけ呼ばれます
	 * @param ModuleManager $manager
	 */
	public function init(ModuleManager $manager);

}

/**
 * モジュール管理のためのインターフェース
 */
interface ModuleManager
{
	/**
	 * @param string $moduleId Module ID
	 * @return Config
	 */
	public function load();

	/**
	 * 指定されたモジュールが存在するかどうかを返します
	 * @param $moduleId
	 * @return bool
	 */
	public function existsModule($moduleId);

	/**
	 * Returns module config.
	 * @param string $moduleId Module ID
	 * @return Config
	 */
	public function getModuleConfig($moduleId);
}

/**
 * @see ModuleManger
 */
class SimpleModuleManager implements ModuleManager
{
	/**
	 * @param string $moduleId Module ID
	 * @return Config
	 */
	public function load()
	{
		// Do nothing.
	}

	/**
	 * @param string $moduleId モジュール名
	 * @return bool
	 * @see ModuleManger#existsModule
	 */
	public function existsModule($moduleId)
	{
		return System::getProperty('Application/Modules/'.$moduleId) !== null;
	}

	/**
	 * @param string $moduleId モジュール名
	 * @return bool
	 * @see ModuleManger#existsModule
	 */
	public function getModuleConfig($moduleId)
	{
		return System::getProperty('Application/Modules/'.$moduleId);
	}
}

/**
 * アプリケーション
 *
 * @author Kenji Namba
 * @version 1.0
 * @since barge 1.2 2008/08/14
 */
class Application
{
	/**
	 * Default router class.
	 */
	const DEFAULT_ROUTER_CLASS = 'barge\web\DefaultRouter';

	/**
	 * Default exception handler class.
	 */
	const DEFAULT_EXCEPTION_HANDLER = 'barge\web\DefaultExceptionHandler';

	/**
	 * アプリケーションのパス
	 * @var string
	 */
	private $basePath;

	/**
	 * アプリケーションワークディレクトリ
	 * @var string
	 * @since 1.4 2009/11/13
	 */
	private $workDir;

	/**
	 * @var Router
	 */
	private $router;

	/**
	 * @since 1.4 2009/11/07
	 */
	public static function load($path = '/', $config = 'app-config.xml')
	{
		return new self($path, $config);
	}

	/**
	 * コンストラクタ
	 *
	 * @param string $appbase アプリケーションルートディレクトリをシステムルートディレクトリー
	 * 			からの相対パスで指定する、通常は webapps ディレクトリーなどからの相対パスになります
	 * @param string $config
	 * @since barge 1.2
	 */
	private function __construct($path = '/', $config = null)
	{
		if ($path[0] !== '/') $path = '/'.$path;
		$this->basePath = $path;

		// Check existing application path.
		$appbase = $this->getRealPath('/');
		if (! is_dir($appbase)) {
			$message = "アプリケーションディレクトリ {$appbase} が存在しません。";
			throw new Exception($message);
		}

		// Set default configulation.
		System::loadProperty($this->getRealPath($config));

		// Note : ワークディレクトリにはデフォルトは適応しない
		$workdir = System::getProperty('Application/workdir');
		if ($workdir === null) {
			$this->workDir = System::getWorkDir();
		} else {
			$this->workDir = $this->getRealPath($workdir);
			if (!file_exists($this->workDir)) {
				@mkdir($this->workDir);
			}
		}
//		// Set work directory.
//		System::setWorkDir($this->getRealPath('/work'));

		$classLoader = System::getClassLoader();
		$config = System::getProperty('Application');
		if ($config === null) {
			$message = "プロパティーエントリー 'Application' が見つかりません。";
			throw new Exception($message);
		}

		$includes = $config->getProperty('defaultIncludes');
		if (System::isNotEmpty($includes)) {
			$classLoader = System::getClassLoader();
			$arr = explode(",", $includes);
			foreach ($arr as $temp) {
				$classLoader->addIncludePath(self::getRealPath($temp));
			}
		}

//		// Execute init handler.
//		$inithandlers = $config->getProperty('initHandlerClass');
//		if (System::isNotEmpty($inithandlers)) {
//			$classLoader = System::getClassLoader();
//			$initHandler = $classLoader->newInstance($inithandlers);
//			$initHandler->init();
//		}
	}

	/**
	 * アプリケーションルートからの相対パスを絶対パスに変換する
	 *
	 * @param string $path 相対パス文字列
	 * @return string 絶対パス文字列
	 * @since 1.2 2008/08/11
	 */
	public function getRealPath($path)
	{
		if ($path[0] === '/')
			$path = substr($path, 1);
		$basedir = System::getBaseDir();
		return ($this->getBasePath() === null) ?
			($basedir.'/'.$path) :
			($basedir.$this->getBasePath().'/'.$path);
	}

	/**
	 * アクション起動のためのURLを生成する
	 *
	 * @param $moduleId モジュールＩＤ
	 * @param $actionId アクションＩＤ
	 * @return string URL
	 * @since 1.0
	 */
	public function createActionURL($moduleId, $actionId)
	{
		$urlformat = System::getProperty('Application/urlPattern');
		if ($urlformat === null) {
			$message = "URLパターン 'url_pattern' が設定されていません。";
			throw new Exception($message);
		}
		$basepath = '';
		if (substr($urlformat, 0, 4) !== 'http') {
			$basepath = $this->getBaseURI();
			if ($basepath === '/' && $urlformat[0] === '/') {
				$basepath = '';
			} else if ($basepath !== '/' &&
				$urlformat[0] !== '/' &&
				$basepath[strlen($basepath) - 1] !== '') {
				$basepath = $basepath . '/';
			}
		}
	    return $basepath . String::format($urlformat,
	    	Array('module_id'=>$moduleId, 'action_id'=>$actionId));
	}

	/**
	 * 相対URLから絶対URLを作成する
	 *
	 * @param string $filepath ファイルパス
	 * @return string
	 * @since 1.0
	 */
	public function createURL($filepath)
	{
		$basepath = $this->getBaseURI();
		if ($basepath === '/')
			$basepath = '';
		if ($filepath[0] !== '/')
			$filepath = '/' .$filepath;
		return $basepath.$filepath;
	}

	/**
	 * アプリケーションのパスを返します。これはURLパスではなく内部パスになります
	 * @return string /test のようなアプリケーションパス
	 * @since 1.0
	 */
	public function getBasePath()
	{
		return $this->basePath;
	}

	/**
	 * アプリケーションワークディレクトリを返します
	 * @return string
	 * @since 1.4 2009/11/13
	 */
	public function getWorkDir()
	{
		return $this->workDir;
	}

	/**
	 * Exit application.
	 *
	 * @param int $status Status code.
	 * @since 1.2
	 */
	public function end($status = 0)
	{
		exit($status);
	}

	/**
	 * アプリケーションのベースURLを返します。このパスはApacheによってマッピングされる
	 * 仮想パスが設定されます。
	 *
	 * @param string Request URI
	 * @return string ex. "/", "/sample"
	 * @since 1.0 2008/10/16
	 */
	public function getBaseURI()
	{
		$basepath = dirname($_SERVER['SCRIPT_NAME']);
		return str_replace('\\', '/', $basepath);
	}

	/**
	 * Processing request.
	 *
	 * @since 1.0
	 */
	public function process()
	{
		$classLoader = System::getClassLoader();
		$context = new ActionContext($this, new HttpRequest(), new Response());

		try {
			// Create router.
			$routerClass = System::getProperty('Application/routerClass', self::DEFAULT_ROUTER_CLASS);
			$this->router = $classLoader->newInstance($routerClass);
			$this->router->dispatch($context);
		} catch (Exception $e) {
			$exceptionHandlerClass = System::getProperty('Application/exception-handler/class', self::DEFAULT_EXCEPTION_HANDLER);
			$exceptionHandler = $classLoader->newInstance($exceptionHandlerClass);
			$exceptionHandler->execute($context, $e);
		}
	}
}

///**
// * 初期化処理ハンドラーインターフェース
// * <p/>
// * 初期化ハンドラーはアプリケーション実行の最初のタイミングで実行されます。
// *
// * @author Kenji Namba
// * @version 1.0
// * @since 1.0 2006/08/12
// */
//interface InitHandler
//{
//	public function init();
//}


/**
 * 例外ハンドラーインターフェース
 * <p/>
 * 例外ハンドラーは、アプリケーションで発生した例外(Exception)を一括して処理する機構を提供
 * します。
 *
 * @author Kenji Namba
 * @version 1.0
 * @since 1.0 2006/08/12
 * @see DefaultExceptionHandler
 */
interface ExceptionHandler
{
	/**
	 * @param ActionContext $context
	 * @param Exception $e
	 */
	public function execute(ActionContext $context, Exception $e);
}

/**
 * デフォルトの例外ハンドラ実装
 *
 * @version 1.1
 * @since barge 1.0 2006/08/12
 * @see ExceptionHandler
 */
class DefaultExceptionHandler implements ExceptionHandler
{
	/**
	 * @param ActionContext $context
	 * @param Exception $e
	 * @see ExceptionHandler#execute()
	 */
	public function execute(ActionContext $context, Exception $e)
	{
		try {
			// Since 1.1
			header('HTTP/1.1 500 Internal Server Error');
			$log = LogFactory::getLogger();
			$log->error(ExceptionUtils::getTraceAsString($e));
			print('<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8">');
			print('</head><style type="text/css">h1 {font-family:Arial;}</style><body>');
			print('<h1>500 Internal server error.</h1>');
			print('<pre style="font-weight:normal;">' . ExceptionUtils::getTraceAsString($e) . '</pre>');
			print('<p><small><i>注意：この例外ハンドラーは開発用です</i></small></p>');
			print('</body></html>');
		} catch (Exception $e) {
			try {
				System::err()->write(ExceptionUtils::getTraceAsString($e));
			} catch (Exception $e) {}
		}
	}
}

/**
 *
 * @version 1.0
 * @author Kenji Namba
 * @since 1.0 2008/05/12
 */
class User extends Bean
{
	const ALL = "all";

	/**
	 * @var HashMap
	 */
	private $credentials;

	/**
	 */
	public function __construct()
	{
		$this->clearCredentials();
	}

	/**
	 * @return void
	 */
	public function init()
	{
		// Do nothing.
	}

	/**
	 * @return void
	 */
	public function clearCredentials()
	{
		$this->credentials = new HashMap();
		$this->init();
	}

	/**
	 * @param module
	 * @param action
	 * @return boolean
	 */
	public function hasCredential($module, $action = null)
	{
		$actionMap = $this->credentials->get($module);
		if ($actionMap == null) {
			return false;
		} elseif ($action == null || $actionMap === self::ALL) {
			return true;
		} else {
			return $actionMap->contains($action);
		}
	}

	/**
	 * @param string $module
	 * @param string $action
	 */
	public function addCredential($module, $action = null)
	{
		$actionMap = $this->credentials->get($module);
		if ($actionMap !== null && $actionMap instanceof HashMap) {
			if ($action !== null) {
				$actionMap->put($action, self::ALL);
			} else {
				$this->credentials->put($module, self::ALL);
			}
		} else {
			if ($action !== null) {
				$actionMap = new HashMap();
				$actionMap->put($action, self::ALL);
				$this->credentials->put($module, $actionMap);
			} else {
				$this->credentials->put($module, self::ALL);
			}
		}
	}
}

/**
 * @version 1.0
 * @since Barge 1.2
 */
class ActionContext
{
	/*
	 * @var string
	 */
	const USER_KEY = "User";

	/*
	 * @var string
	 */
	const DEFAULT_MODULE_DIR = "/modules";

	/**
	 * @var string
	 */
	const DEFAULT_USER_CLASS = "barge\web\User";

	/**
	 * @var string
	 */
	const DEFAULT_MODULE = '';

	/**
	 * @var string
	 */
	const DEFAULT_ACTION = '';

	/**
	 * @var Request
	 */
	protected $request;

	/**
	 * アプリケーションインスタンス
	 * @var Application
	 */
	protected $application;

	/*
	 * @var Response
	 */
	protected $response;

	/*
	 * @var string
	 */
	protected $moduleId;

	/*
	 * @var string
	 */
	protected $actionId;

//	/**
//	 * @var AuthHandler
//	 */
//	protected $authHandler;

	/**
	 * @var User
	 */
	protected $user;

	/**
	 * @var BeanManager
	 */
	protected $beanManager;

	/**
	 * @var Messages
	 */
	protected $messages;

	/**
	 * @var Messages
	 */
	protected $errors;

	/**
	 * @var HashMap
	 */
	protected $formCache;

	/**
	 * @var ArrayList
	 */
	protected $actionFilters;

	/**
	 * @var HashMap
	 */
	protected $contextConfig;

	/**
	 * @var ModuleManager
	 */
	protected $moduleManager;

	/**
	 * A constructor.
	 *
	 * @param Application $application application instance.
	 * @param Request $request
	 * @param Response $response
	 * @since 1.2 2008/08/30
	 */
	public function __construct(Application $application, Request $request, Response $response)
	{
		$this->request = $request;
		$this->response = $response;
		$this->application = $application;

		$this->contextConfig = System::getProperty('Application');
		$this->beanManager = BeanManager::getInstance();
		$this->formCache = new HashMap();
//		$this->moduleManager = ModuleManager::load($this->application->getRealPath('/'));
		$this->actionFilters = $this->loadActionFilters();

		$this->moduleManager = $this->instanceModuleManager();
		$this->moduleManager->load();
	}

	/**
	 * @return ModuleManger
	 */
	protected function instanceModuleManager()
	{
		$classLoader = System::getClassLoader();
		if (System::getProperty('Application/multipleModule') === 'true') {
			$modulepath = System::getProperty('Application/moduleDir', self::DEFAULT_MODULE_DIR);
			$moduledir = $this->application->getRealPath($modulepath);
			return new \barge\web\multiple\MultipleModuleManager($moduledir);
		} else {
			return new SimpleModuleManager();
		}
	}

	/**
	 * アプリケーションレベルで指定されたフィルターをロードする
	 * @return ArrayList
	 */
	protected function loadActionFilters()
	{
		// barge.iniで定義されたフィルタ
		$actionFilters = new ArrayList();
		$filterClasses = $this->contextConfig->getProperty('actionFilters');
		if (System::isNotEmpty($filterClasses)) {
			foreach ($filterClasses as $filterClass) {
				$actionFilters->add(new $filterClass());
			}
		}
		// モジュールで定義されたフィルタを追加
//		$actionFilters->addAll($this->moduleManager->getActionFilters());
		return $actionFilters;
	}

	/**
	 * アプリケーションインスタンスを取得する
	 * @return Application
	 */
	public function getApplication()
	{
		return $this->application;
	}

	/**
	 * ワークディレクトリーを取得します
	 *
	 * @param string $moduleId 別モジュールの場合は、モジュールキー
	 * @return string
	 */
	public function getWorkDir($moduleId = null)
	{
		if ($moduleId === null) {
			$moduleId = $this->moduleId;
		}
		$modulesDir = $this->application->getWorkDir() . '/modules';
		if (!File::exists($modulesDir)) {
			File::makeDir($modulesDir);
		}
		$file = $modulesDir.'/'.$moduleId;
		if (!File::exists($file)) {
			File::makeDir($file);
		}
		return $file;
	}

	/**
	 * <pre>
	 * $context->forward('list');
	 * </pre>
	 *
	 * @param string $actionId アクションのキー
	 * @param string $moduleId 別モジュールの場合は、モジュールキー
	 */
	public function forward($actionId, $moduleId = null)
	{
		if ($moduleId === null) {
			$moduleId = $this->getModuleId();
		} else if ($moduleId === '') {
			$moduleId = System::getProperty('Application/defaultModuleId');
		}

		/* @var $moduleConfig Config */
		$moduleConfig = $this->moduleManager->getModuleConfig($moduleId);
		if ($moduleConfig === null) {
			$message = "フォーワード先のモジュール '{$moduleId}' が見つかりません。";
			throw new IllegalArgumentException($message);
		}

		if ($actionId === null) {
			$message = "アクションキーが指定されていません。";
			throw new IllegalArgumentException($message);
		} else if ($actionId === '') {
			$actionId = $moduleConfig->getProperty('defaultActionId');
			if ($actionId === null) {
				$actionId = System::getProperty('Application/defaultActionId');
			}
		}

		$actionMapping = $moduleConfig->getProperty('ActionMapping');
		if ($actionMapping === null) {
			$actionPattern = $moduleConfig->getProperty('actionPattern');
//			$actionPattern = $moduleManager->getActionPattern($moduleId);
			if ($actionPattern === null) {
				$message = "指定されたアクションが存在しません。moduleId={$moduleId} actionId={$actionId}";
				throw new Exception($message);
			}
			$actionClass = String::format($actionPattern, Array(
				'module_id'=>$moduleId,
				'action_id'=>$actionId) );
		} else {
			// TODO : ActionMapping からクラスを取得する
//			$actionClass = $actionMapping->getActionClass();
		}

		// アクションのインスタンスを生成
		$classLoader = System::getClassLoader();
		$actionInstance = $classLoader->newInstance($actionClass);

		// 現在のアクションを設定する
		$this->moduleId = $moduleId;
		$this->actionId = $actionId;

		$log = LogFactory::getLogger();
		$log->info(" => Forward action '{$moduleId}/{$actionId}'");

		// フィルターの実行
		$filterChain = new ActionFilterChain($this, $this->actionFilters, $actionInstance);
		$filterChain->doFilter();
	}

	/**
	 * アクションにリダイレクト
	 *
	 * <pre>
	 * $context->redirect("top");
	 * $context->redirect("", "module");
	 * </pre>
	 * @param string $actionId
	 * @param string $moduleId
	 * @param HashMap $params パラメータマップ
	 */
	public function redirect($actionId = null, $moduleId = null, HashMap $params = null)
	{
//		$moduleManager = $this->moduleManager;
		if ($moduleId === null) {
			$moduleId = $this->getModuleId();
		} else if ($moduleId === '') {
			$moduleId = System::getProperty('Application/defaultModuleId');
		}

		if ($actionId === null) {
			$moduleId = $this->getModuleId();
		} else if ($actionId === '') {
			$actionId = $moduleConfig->getProperty('defaultActionId');
			if ($actionId === null) {
				$actionId = System::getProperty('Application', 'defaultActionId');
			}
		}

		// URLを作成
		$url = $this->application->createActionURL($moduleId, $actionId);
		if ($params !== null) {
			if (strpos($url, '?') !== false) {
				$url .= '&'.URLUtils::toQueryString($params);
			} else {
				$url .= '?'.URLUtils::toQueryString($params);
			}
		}
		$log = LogFactory::getLogger();
		$log->debug('Redirect url : '.$url);

		$this->getResponse()->redirect($url);
	}

//
//	/**
//	 * 認証ハンドラのインスタンスを生成して返す
//	 * @return AuthHandler
//	 */
//	protected function instanceAuthHandler()
//	{
//		$authHandlerClass = System::getProperty('auth_handler_class');
//		if ($authHandlerClass === null || strlen($authHandlerClass) == 0) {
//			return null;
//		}
//		$classLoader = System::getClassLoader();
//		return $classLoader->newInstance($authHandlerClass);
//	}

	/**
	 * メッセージの一覧を保存する
	 * @param Messages $messages
	 */
	public function saveMessages(Messages $messages)
	{
		$this->messages = $messages;
	}

	const TOKEN_KEY = 'barge_token_key';

	/**
	 * @since 1.2.1 2009/03/25
	 */
	public function saveToken()
	{
		$session = $this->request->getSession();
		$session->setAttribute(
			self::TOKEN_KEY, md5(intval(System::currentTime()->format("U"))));
	}

	/**
	 * @return string
	 * @since 1.2.1 2009/03/25
	 */
	public function getTokenId()
	{
		$session = $this->request->getSession();
		return $session->getAttribute(self::TOKEN_KEY);
	}

	/**
	 * @return bool
	 * @since 1.2.1 2009/03/25
	 */
	public function validateToken()
	{
		$token = $this->request->getParameter(self::TOKEN_KEY);
		if ($token === null) {
			return false;
		}
		$session = $this->request->getSession();
		$servertoken = $session->getAttribute(self::TOKEN_KEY);
		if ($token !== $servertoken) {
			return false;
		} else {
			return true;
		}
	}

	/**
	 * メッセージを保存する
	 * @param Message $message
	 */
	public function saveMessage(Message $message)
	{
		$messages = new Messages();
		$messages->add($message);
		$this->messages = $messages;
	}

	/**
	 * @return Messages
	 */
	public function getMessages()
	{
		return $this->messages;
	}

	/**
	 * @return Messages
	 */
	public function getErrors()
	{
		return $this->errors;
	}

	/**
	 * Bean管理クラスを取得する
	 * @param $id
	 * {@return object}
	 */
	public function getBean($id)
	{
		return $this->beanManager->getBean($id);
	}

	/**
	 * モジュールが存在するかどうかを返します
	 * @param string $moduleId モジュールID
	 * @return bool
	 */
	public function existsModule($moduleId)
	{
		return $this->moduleManager->existsModule($moduleId);
	}

	/**
	 * <code>beanManager</code>のためのオーバーロード
	 * <p/>
	 * <code>get*</code>で始まるメソッドが呼ばれ、<code>ActionContext</code>クラスで
	 * 解決できなかった場合、このメソッドは<code>beanManager</code>オブジェクトから生成
	 * を試みます。
	 * @return mixed Bean管理クラスが管理するオブジェクト
	 * @throws IllegalAccessException Bean管理クラスにオブジェクトが存在しない場合に
	 * 		例外をスローします
	 */
	public function __call($name, $arguments)
	{
		LogFactory::getLogger()->debug('Call direct bean access : '.$name);
		if (substr($name, 0, 3) == 'get') {
			$bean = substr($name, 3);
			return $this->beanManager->getBean($bean);
		} else {
			$message = "無効なメソッド'{$name}'が呼ばれました。(autoload)";
			throw new IllegalAccessException($message);
		}
	}

	/**
	 * @return strnig
	 */
	public function getActionId()
	{
		return $this->actionId;
	}

	/**
	 * @return Request
	 */
	public function getRequest()
	{
		return $this->request;
	}

	/**
	 * @return Response
	 */
	public function getResponse()
	{
		return $this->response;
	}

	/**
	 * @return User
	 */
	public function getUser()
	{
		// セッションから検索
		$session = $this->getRequest()->getSession();
		$this->user = $session->getAttribute(self::USER_KEY);
		if ($this->user === null) {
			// ユーザー情報を生成
			$userClass = System::firstNotEmpty($this->contextConfig->getProperty('defaultUserClass'), self::DEFAULT_USER_CLASS);
			$classLoader = System::getClassLoader();
			$this->user = $classLoader->newInstance($userClass);
			// セッション上に配置
			$session->setAttribute(self::USER_KEY, $this->user);
		}
		return $this->user;
	}

	/**
	 * フォームを生成します
	 * <p/>
	 * パラメータによりフォームの格納スコープを制御できます。またこのタイミングで
	 * <code>ActionForm#reset</code>が起動します。
	 * リクエストパラメータは自動的にフォームに設定されます。
	 *
	 * @param bool $session = false フォームをセッションからロードするかどうか
	 * 						デフォルトは、<code>false</code>
	 * @param string $formName = null フォーム名、省略された場合は現在アクティブなアクションに対
	 * 						応するフォームが生成されます。
	 * @param bool $create = true フォームが存在しない場合に自動的に作成するかどうか
	 * {@return ActionForm}
	 */
	public function getForm($session = false, $formName = null, $create = true)
	{
		/* @var $config Config */
		// TODO : モジュール化
		$config = $this->moduleManager->getModuleConfig($this->moduleId);
		$request = $this->request;

		if ($formName === null) {
			$pattern = $config->getProperty('defaultFormNamePattern');
			if ($pattern === null) {
				$message = "フォームの指定が存在しません。module={$this->moduleId}";
				throw new Exception($message);
			}
			$formName = String::format($pattern, Array(
				'action_id'=>$this->actionId) );
		}

		// フォーム名を取得する
		$pattern = $config->getProperty('formPattern');
		$formClass = String::format($pattern, array('form_name'=>$formName ));

		// キャッシュから取得する
		if ($this->formCache->contains($formName)) {
			return $this->formCache->get($formName);
		}

		$classLoader = System::getClassLoader();

		/* @var $formInstance ActionForm */
		$formInstance = null;
		if ($session) {
			$session = $this->getRequest()->getSession();
			$formInstance = $session->getAttribute($formName);
			if ($formInstance === null) {
				if ($create) {
					// フォームのインスタンスを生成
					$formInstance = $classLoader->newInstance($formClass);
					// セッションに設定
					$session->setAttribute($formName, $formInstance);
				} else {
					return null;
				}
			}
		} else {
			$formInstance = $request->getAttribute($formName);
			if ($formInstance === null) {
				// フォームのインスタンスを生成
				if ($create) {
					$classLoader = System::getClassLoader();
					$formInstance = $classLoader->newInstance($formClass);
					$request->setAttribute($formName, $formInstance);
				} else {
					return null;
				}
			}
		}

		// リクエストパラメーターを反映
		if ($create) {
			$formInstance->reset($request);
			$formInstance->populate($request->getParameters());
		}

		$this->formCache->put($formName, $formInstance);
		return $formInstance;
	}

	/**
	 * フォームをすべてのスコープから削除します
	 * @param string $formClass フォーム名、省略された場合は現在アクティブなアクションに対
	 * 						応するフォームが生成されます。
	 * @since 1.0 2008/09/16
	 */
	public function removeForm($formClass = null)
	{
		if ($formClass === null) {
		// TODO : モジュール化
			$config = System::getProperty('Application/Modules/'.$this->moduleId);
			$formPattern = $config->getProperty('formPattern');
			if ($formPattern === null) {
					$message = "フォームの指定が存在しません。module={$this->moduleId}";
					throw new Exception($message);
			}
			$formClass = String::format($formPattern, Array(
					'module_id'=>$this->moduleId,
					'action_id'=>$this->actionId) );
		}

		$this->request->setAttribute($formName, null);
		$session = $this->request->getSession();
		$session->setAttribute($formName, null);
	}

//	/**
//	 * レンダラーを生成します
//	 *
//	 * @param string templatePath レンダラーに初期値として与えるテンプレートパス
//	 * @return Renderer
//	 */
//	public function getRenderer($templatePath = null)
//	{
//		$rendererClass = System::getProperty('ActionContext', 'default_renderer_class');
//		$classLoader = System::getClassLoader();
//		$rendererInstance = $classLoader->newInstance($rendererClass, new ArrayList(basename($templatePath)));
//		$rendererInstance->setBaseDir(dirname($templatePath).'/');
//		$rendererInstance->setContext($this);
//		return $rendererInstance;
//	}

	/**
	 * ページ表示のためのレンダラを起動します
	 *
	 * <pre>
	 * $context->render();
	 * $context->render('/src/templates/top.tpl');
	 * $context->render('/src/templates/top.xml.tpl', 'application/xml');
	 * </pre>
	 *
	 * @param string $path
	 * @param string $contenttype
	 */
	public function render($path = null, $contenttype = 'text/html')
	{
		// TODO : モジュール化
		$config = System::getProperty('Application/Modules/'.$this->moduleId);

		if ($path === null) {
			$template = $config->getProperty('templatePattern');
			if ($template !== null) {
				$path = String::format($template,
					Array('action_id'=>$this->actionId, 'module_id'=>$this->moduleId));
			} else {
				$message = "テンプレートファイルパスが設定されていません。 モジュール:{$this->moduleId}";
				throw new FileNotFoundException($message);
			}
//			$this->log->debug($template);
		}

		if ($path === null) {
			$message = "テンプレートファイルパスが設定されていません。 モジュール:{$this->moduleId}";
			throw new FileNotFoundException($message);
		}

		$rendererClass = System::getProperty('Application/renderer/class');

		// Get base directory.
		$baseidr = $config->getProperty('templateBase');
		if (empty($baseidr)) {
			$baseidr = '/';
		}
		$basedir = $this->application->getRealPath($baseidr);

		$classLoader = System::getClassLoader();
		/* @var $renderer Renderer */
		$renderer = $classLoader->newInstance($rendererClass, new ArrayList($path));
		$renderer->setBaseDir($basedir);
		$renderer->setContext($this);
		$renderer->setContentType($contenttype);
		$renderer->render();
	}

	/**
	 * 現在のモジュールIDを取得します
	 * @return string
	 */
	public function getModuleId()
	{
		return $this->moduleId;
	}
}


/**
 * アクションフィルター
 *
 * @author Kenji Namba
 * @version 1.0
 * @since 1.2 2008/08/12
 */
interface ActionFilter
{
	/**
	 * @param ActionContext $context
	 * @param ActionFilterChain $filterChain
	 */
	public function filter(ActionContext $context,
		ActionFilterChain $filterChain);
}

/**
 *
 * @author Kenji Namba
 * @version 1.0
 * @since 1.2 2008/08/12
 */
class ActionFilterChain
{
	/**
	 * @var ArrayIterator
	 */
	private $iterator;

	/**
	 * @var ActionContext
	 */
	private $context;

	/**
	 * @var Action
	 */
	private $action;

	/**
	 * @param ActionContext $context
	 * @param ArrayList $list
	 * @param Action $action
	 */
	public function __construct(ActionContext $context, ArrayList $list, Action $action)
	{
		$this->iterator = $list->getIterator();
		$this->context = $context;
		$this->action = $action;
	}

	/**
	 *
	 */
	public function doFilter()
	{
		if ($this->iterator->valid()) {
			$obj = $this->iterator->current();
			$this->iterator->next();
			$obj->filter($this->context, $this);
		} else {
			$this->action->execute($this->context);
		}

	}
}

/**
 *
 * @author Kenji Namba
 * @version 1.0
 * @since 1.2 2008/05/12
 */
interface Router
{
	/**
	 * @param ActionContext $context
	 * @since 1.0 2008/05/12
	 */
	public function dispatch(ActionContext $context);
}

/**
 *
 * @author Kenji Namba
 * @version 1.0 2008/05/12
 * @since Barge 1.0 2008/05/12
 * @see Router
 */
class DefaultRouter implements Router
{
	/**
	 * @param ActionContext $context
	 * @since 1.0 2008/07/11
	 */
	public function dispatch(ActionContext $context)
	{
		list ($moduleId, $actionId) = $this->processPath($context->getRequest());

		$log = LogFactory::getLogger();
		$log->info('=> Request action moduleId='.$moduleId.' actionId='.$actionId);

		// URLパラメータチェック
		// Note : '' は規定のアクション
		if (($actionId === '' || preg_match('/^[^_][A-Za-z0-9_\.]+$/', $actionId)) &&
			($moduleId === '' || preg_match('/^[^_][A-Za-z0-9_\.]+$/', $moduleId))) {
			$context->forward($actionId, $moduleId);
		} else {
			$message = "許可されていないURLです。";
			throw new IllegalAccessException($message);
		}
	}

	/**
	 * リクエストより処理キーを取得します
	 *
	 * @param Request $request
	 * @return array [Module ID, Action Id]
	 * @since 1.0 2008/07/11
	 */
	protected function processPath(Request $request)
	{
		$actionId = '';
		$moduleId = '';
		$pattern = System::getProperty('Application/urlPattern');

		$query = null;
		$path = null;
		$p = strpos($pattern, '?');
		if ($p !== false) {
			$path = substr($pattern, 0, $p + 1);
			$query = substr($pattern, $p + 1);
		} else {
			$path = $pattern;
		}

		if (!empty($path)) {
			$array1 = explode('/', $path);
			$array2 = explode('/', $request->getPathInfo());
			for ($i = 0; $i < count($array1) && $i < count($array2); $i++) {
				if (strpos($array1[$i], '{action_id') !== false) {
					$actionId = $array2[$i];
				} else if (strpos($array1[$i], '{module_id') !== false) {
					$moduleId = $array2[$i];
				}
			}
		}

		if (!empty($query)) {
			$params2 = $request->getQueryParams();
			$params = explode('&', $query);
			foreach ($params as $param) {
				$p = strpos($param, '=');
				if ($p !== false) {
					if (strpos($param, '{action_id', $p) !== false) {
						$value = $params2->get(substr($param, 0, $p));
						if ($value != null)
							$actionId = $value;
					} else if (strpos($param, '{module_id', $p) !== false) {
						$value = $params2->get(substr($param, 0, $p));
						if ($value != null)
							$moduleId = $value;
					}
				}
			}
		}

		return array(
			$moduleId,
			$actionId
			);
	}
}

/**
 * @version 1.0
 * @since Barge 1.1 2006/10/17
 */
class Response
{
	/**
	 * @var OutputStream
	 */
	private $out;

	/**
	 * @since 1.0 2006/10/17
	 */
	public function __construct()
	{
		$this->out = System::out();
	}

	/**
	 * @param string $url URL string
	 * @since 1.0 2006/10/17
	 */
	public function redirect($url)
	{
		if (strpos($url, '\n') !== false) {
			$message = "Invalidate header.";
			throw new IllegalAccessException($message);
		}
		header("Location: $url");
		System::end(0);
	}

	/**
	 * @param string $key
	 * @param string $value
	 * @param bool $uriEncode
	 * @since 1.0 2006/10/17
	 */
	public function sendHeader($key, $value, $uriEncode = null)
	{
		// TODO : URLエンコード
		header($key.': '.$value);
	}

	/**
	 * @param mixed $value
	 * @since 1.0 2006/10/17
	 */
	public function write($value)
	{
		$this->out->write((string)$value);
		$this->out->flush();
	}

	/**
	 * @param Cookie $cookie
	 * @since 1.0 2006/07/09
	 */
	public function setCookie(Cookie $cookie)
	{
		$cookies = System::getProperty('Application/cookie');
		$path = ($cookie->getPath() === null ?
			($cookies->getProperty('path') === null ? '/' : $cookies->getProperty('path')) : $cookie->getPath());
		$domain = ($cookie->getDomain() === null ?
		($cookies->getProperty('domain') === null ? null : $cookies->getProperty('domain')) :
		$cookie->getDomain());
		$expire = ($cookie->getExpire() === null ?
		($cookies->getProperty('expire') === null ? null : $cookies->getProperty('expire')) :
		$cookie->getExpire());

//var_dump(CookieSerializer::serialize($cookie));
		setcookie($cookie->getName(), CookieSerializer::serialize($cookie), $expire, $path, $domain);
	}

	/**
	 * @param Cookie $cookie
	 * @since 1.0 2006/07/09
	 */
	public function removeCookie(Cookie $cookie)
	{
		$cookies = System::getProperty('Application/cookie');
		$path = ($cookie->getPath() === null ?
			($cookies->getProperty("path") === null ? '/' : $cookies->getProperty("path")) : $cookie->getPath());
		setcookie($cookie->getName(), null, null, $path);
	}

}

/**
 *
 * @version 1.0
 * @author Kenji Namba
 */
class Cookie
{

	const DEFAULT_COOKIE = "default";
	const UNIT_DAY = 1;

	/**
	 * @var HashMap
	 */
	private $values;

	/**
	 * @var int
	 */
	private $expire;

	/**
	 * @var string
	 */
	private $name;

	/**
	 * @var string
	 */
	private $path = null;

	/**
	 * @var boolean
	 */
	private $secure = false;

	/**
	 * @var boolean
	 */
	private $domain = null;

	/**
	 * @param string $name
	 * @param string $expire
	 * @param string $path
	 * @param string $domain
	 * @since 1.0 2006/07/09
	 */
	public function __construct($name = self::DEFAULT_COOKIE, $expire = -1, $path = null, $domain = null)
	{
		// Default expir 1 month
		$this->expire = $expire;
		$this->name = $name;
		$this->path = $path;
		$this->domain = $domain;
		$this->values = new HashMap();
	}

	/**
	 * @param string $name
	 * @return miexed
	 * @since 1.0 2006/07/09
	 */
	public function getValue($name)
	{
		return $this->values->get($name);
	}

	/**
	 * @param string $name
	 * @param mixed $value
	 */
	public function setValue($name, $value)
	{
		$this->values->put($name, $value);
	}

	/**
	 * @return ArrayList
	 */
	public function getNames()
	{
		return $this->values->keys();
	}

	/**
	 * @param long $time
	 * @param int $unit
	 * @return long
	 */
	public function setExpire($time, $unit = 1)
	{
		$this->expire = time() + 60 * 60 * 24 * $time;
	}

	/**
	 * @return long
	 */
	public function getExpire()
	{
		return $this->expire;
	}

	/**
	 * @return string
	 */
	public function getName()
	{
		return $this->name;
	}

	/**
	 * @param string
	 */
	public function setPath($path)
	{
		$this->path = $path;
	}

	/**
	 * @return string
	 */
	public function getPath()
	{
		return $this->path;
	}

	/**
	 * @param string $domain
	 */
	public function setDomain($domain)
	{
		$this->domain = $domain;
	}

	/**
	 * @return string
	 */
	public function getDomain()
	{
		return $this->domain;
	}
}

/**
 * リクエストオブジェクト
 * <p/>
 * $_GET, $_POSTのラッパーとして動作します
 *
 * @author Kenji Namba
 * @version 1.0
 * @since Barge 1.0 - 2006/08/12
 */
interface Request
{
	/**
	 * @param string $name
	 * @return Cookie
	 */
	public function getCookie($name = null);

	/**
	 *
	 * @return HashMap
	 * @since 1.0 2006/08/12
	 */
	public function getQueryParams();

	/**
	 * Rturns 'GET' or 'POST'
	 *
	 * @return string
	 * @since 1.0 2006/08/12
	 */
	public function getRequestMethod();

	/**
	 * @return string
	 * @since 1.0 2006/08/12
	 */
	public function getContentType();

	/**
	 * Get parameter value from parameter name.
	 *
	 * @param string $name
	 * @return mixed
	 * @since 1.0 2006/08/12
	 */
	public function getParameter($name);

	/**
	 * パラメータの一覧を取得します
	 *
	 * @return HashMap
	 * @since 1.0 2006/10/15
	 */
	public function getParameters();

	/**
	 * @param string $key
	 * @return mixed
	 * @since 1.0 2006/10/15
	 */
	public function getAttribute($key);

	/**
	 * @param string key
	 * @param mixed $object
	 * @return mixed
	 * @since 1.0 2006/10/15
	 */
	public function setAttribute($key, $object);

	/**
	 * @return Session
	 * @since 1.0 2006/10/15
	 */
	public function getSession();

	/**
	 * @param string $name
	 * @return string
	 * @since 1.0 2006/10/16
	 */
	public function getHeader($name);

	/**
	 * @return string
	 * @since 1.0 2008/10/16
	 */
	public function getRequestURI();

//	/**
//	 * @return string
//	 * @since 1.0 2008/10/16
//	 */
//	public function getBaseURI();

	/**
	 * @return string
	 * @since 1.0 2008/10/16
	 */
	public function getPathInfo();
}

/**
 * リクエストオブジェクト
 * <p/>
 * <code>$_GET</code>, <code>$_POST</code>のラッパーとして動作します
 *
 * @author Kenji Namba
 * @version 1.0
 * @since Barge 1.0 2006/08/12
 */
class HttpRequest implements Request
{
	/**
	 * @var HashMap
	 * @since 1.0 2006/08/12
	 */
	protected $parameters;

	/**
	 * @var HashMap
	 * @since 1.0 2006/08/12
	 */
	protected $queryParams;

	/**
	 * @var string Request method string.
	 * @since 1.0 2006/08/12
	 */
	protected $requestMethod;

	/**
	 * @var HashMap
	 * @since 1.0 2006/08/12
	 */
	protected $attributes;

	/**
	 * @var Session
	 * @since 1.0 2006/08/12
	 */
	protected $session = null;

	/**
	 * @var HashMap
	 * @since 1.0 2006/10/18
	 */
	protected $headers;

	/**
	 * @var string
	 * @since 1.0 2006/10/18
	 */
	protected $requestURI;

	/**
	 * @since 1.0 2006/08/12
	 */
	public function __construct()
	{
		$this->requestMethod = isset($_SERVER['REQUEST_METHOD']) ?
			$_SERVER['REQUEST_METHOD'] : null;

		// Get parameters.
		$this->queryParams = new HashMap($_GET);
		$this->attributes = new HashMap();
		$this->headers = new HashMap($_SERVER);
		$this->parameters = new HashMap(
		($this->requestMethod == 'POST') ? $_POST : $_GET);
		// Since 1.3
		if (isset($_FILES)) {
			foreach ($_FILES as $key => $value) {
				if ($value['error'] !== UPLOAD_ERR_NO_FILE) {
					$this->parameters->put($key,
						array(
						'contentType' => $value['type'],
						'fileSize' => $value['size'],
						'fileName' => $value['name'],
						'tempFileName' => $value['tmp_name'],
						'errorNo' => $value['error']
						));
				}
			}
		}
	}

	/**
	 * @param string $name
	 * @return Cookie
	 */
	public function getCookie($name = Cookie::DEFAULT_COOKIE)
	{
		return (isset($_COOKIE[$name]) ?
			CookieSerializer::deserialize($name, $_COOKIE[$name]) : null);
	}

	/**
	 *
	 * @return HashMap
	 * @since 1.0 2006/08/12
	 */
	public function getQueryParams()
	{
		return $this->queryParams;
	}

	/**
	 * Rturns 'GET' or 'POST'
	 * @return string
	 * @since 1.0 2006/08/12
	 */
	public function getRequestMethod()
	{
		return $this->requestMethod;
	}

	/**
	 * @return string
	 * @since 1.0 2006/08/12
	 */
	public function getContentType()
	{
		// FIXME :
		return count($_FILES) > 0 ? 'multipart/form-data'
			: 'application/x-www-form-urlencoded';
	}

	/**
	 * リクエストパラメータを取得します
	 * @param string $name
	 * @return mixed
	 * @since 1.0 2006/08/12
	 */
	public function getParameter($name)
	{
		if (!$this->parameters->contains($name)) {
			return null;
		}
		return $this->parameters->get($name);
	}

	/**
	 * パラメータの一覧を取得します
	 *
	 * @return HashMap
	 * @since 1.0 2006/10/15
	 */
	public function getParameters()
	{
		return $this->parameters;
	}

	/**
	 * @param string key
	 * @return mixed
	 * @since 1.0 2006/10/15
	 */
	public function getAttribute($key)
	{
		return $this->attributes->get($key);
	}

	/**
	 * @return Session
	 * @since 1.0 2006/10/15
	 */
	public function getSession()
	{
		if ($this->session === null) {
			$this->session = new Session();
		}
		return $this->session;
	}

	/**
	 * @param string key
	 * @return mixed
	 * @since 1.0 2006/10/15
	 */
	public function setAttribute($key, $object)
	{
		return $this->attributes->put($key, $object);
	}

	/**
	 * @param string name
	 * @return string
	 * @since 1.0 2006/10/16
	 */
	public function getHeader($name)
	{
		return $this->headers->get($name);
	}

	/**
	 * @param string Request URI
	 * @return string
	 * @since 1.0 2008/10/16
	 */
	public function getRequestURI()
	{
		if ($this->requestURI == null) {
			$uri = $this->headers->get('REQUEST_URI');
			$pos = 0;
			if (($pos = strpos($uri, '?')) !== false) {
				$uri = substr($uri, 0, $pos);
			}
			$this->requestURI = $uri;
		}
		return $this->requestURI;
	}

	/**
	 * @return string
	 * @since 1.0 2008/10/16
	 */
	public function getPathInfo()
	{
		$pathInfo = $this->headers->get('PATH_INFO');
		if ($pathInfo == null) {
			$scriptDir = dirname($this->headers->get('SCRIPT_NAME'));
			$pathInfo = substr($this->getRequestURI(), strlen($scriptDir));
		}
		return $pathInfo;
	}
}

/**
 * Serializer for cookie.
 * @since 1.3 2009/06/12
 * @author Kenji Namba
 */
class CookieSerializer
{
	/**
	 * @param Cookie $cookie
	 */
	public static function serialize(Cookie $cookie)
	{
		// TODO : 型付き
		$query = '';
		foreach ($cookie->getNames() as $name) {
			$query .= ($query != null ? '&' : '') .
			$name .
			'=' .
			urlencode($cookie->getValue($name));
		}
		return base64_encode($query);
	}

	/*
	 * @param $name
	 * @param $value
	 */
	public static function deserialize($name, $value)
	{
		$cookie = new Cookie($name);
		$fields = explode('&', base64_decode($value));
		foreach ($fields as $field) {
			$values = explode('=', $field);
			if (count($values) == 2)
				$cookie->setValue($values[0], urldecode($values[1]));
		}
		return $cookie;
	}
}

/**
 * セッション
 *
 * @author Kenji Namba
 * @version 1.0
 * @since Barge 1.0 2006/08/12
 */
class Session
{
	/**
	 * @var bool
	 */
	protected $started = false;

	/**
	 *
	 * @since 1.0 2006/07/30
	 */
	public function __construct()
	{
		/* @var config HashMap */
		$config = System::getProperty('Application/session');
		if ($config !== null) {
			if (($value = $config->getProperty('path')) !== null)
				ini_set('session.cookie_path', $value);
			if (($value = $config->getProperty('domain')) !== null)
				ini_set('session.cookie_domain', $value);
			if (($value = $config->getProperty('secure')) !== null)
				ini_set('session.cookie_secure', $value);
			if (($value = $config->getProperty('lifetime')) !== null)
				ini_set('session.cookie_lifetime', $value);
			if (($value = $config->getProperty('maxlifetime')) !== null)
				ini_set('session.gc_maxlifetime', $value);
		}
	}

	/**
	 * @since 1.0 2006/07/30
	 */
	public function start()
	{
		if (! $this->started) {
			session_start();
			$this->started = true;
		}
	}

	/**
	 * セッションを破棄します
	 * @since 1.0 2006/07/30
	 */
	public function invalidate()
	{
		session_destroy();
	}

	/**
	 *
	 * @param string $name A session attribute name.
	 * {@return mixed}
	 * @since 1.0 2006/07/30
	 */
	public function getAttribute($name)
	{
		$this->start();
		return isset($_SESSION[$name]) ?
			$_SESSION[$name] : null;
	}

	/**
	 * @param string $name
	 * @param object $obj
	 * @return void
	 * @since 1.0 2006/07/30
	 */
	public function setAttribute($name, $obj)
	{
		$this->start();
		$_SESSION[$name] = $obj;
	}

	/**
	 * Returns session id.
	 * @return string
	 * @since 1.0 2006/07/30
	 */
	public function getId()
	{
		return session_id();
	}

}


/**
 *
 * @author Kenji Namba
 * @version 1.0
 * @since Barge 1.0 2006/08/12
 */
class BeanManager
{
	/**
	 * @var BeanManager
	 */
	private static $instance;

	/**
	 * @var array
	 */
	private $instances;

	/**
	 * @var Array
	 */
	private $beanConfigs;

	/**
	 * @return BeanManager
	 */
	public static function getInstance()
	{
		if (self::$instance === null) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 *
	 */
	protected function __construct()
	{
		$this->beanConfigs = System::getProperty('BeanContainer');
		$this->instances = array();
	}

	/**
	 * @param string $id
	 * @param string $beanClass
	 * @param bool $singlton
	 * @parma HashMap $constructArgs
	 * @parma HashMap $properties
	 */
	public function registerBeanConfig($beanId, $class, $singleton = false,
		HashMap $constructArgs = null, HashMap $properties = null)
	{
		$beanMap = new HashMap();
		$beanMap->put('class', $class);
		$beanMap->put('singleton', $singleton);
		$beanMap->put('constructArgs', $constructArgs);
		$beanMap->put('properties', $properties);
		$this->beanConfigs->put($beanId, $beanMap);
	}

	/**
	 * @param string name
	 * @return mixed
	 * @throws Exception Bean定義が見つからない、または正しく設定が行われていない場合に
	 * 	スローされます
	 */
	public function getBean($name)
	{
		$log = LogFactory::getLogger();

		if ($this->beanConfigs === null || $this->beanConfigs->getProperty($name) === null) {
			$message = "Bean定義'{$name}'が見つかりません。";
			throw new Exception($message);
		}

		$config = $this->beanConfigs->getProperty($name);
		$singlton = $config->getProperty('singleton');
		if ($singlton === null) {
			$singlton = false;
		}

		$instance = null;
		if ($singlton) {
			if (isset($this->instances[$name])) {
				$log->info('BeanManager : Instance bean '.$name.' form cache');
				return $this->instances[$name];
			}
		}

		$class = $config->getProperty('class');
		if ($class === null) {
			$message = "Bean定義'{$name}'にクラスが指定されていません。";
			throw new Exception($message);
		}
		// コンストラクタ
		$args = new ArrayList();
		$arguments = $config->getProperty('constructArgs');
		if ($arguments !== null) {
			$i=0;
			while ($arguments->getProperty('arg'.$i) !== null) {
				$value = $arguments->getProperty('arg'.$i);
				if (substr($value, 0, 2) == '${' && substr($value, -1) == '}') {
					$args->add($this->getBean(substr($value, 2, -1)));
				} else {
					$args->add($value);
				}
				$i++;
			}
		}

		$classloader = System::getClassLoader();
		$instance = $classloader->newInstance($class, $args);

		// プロパティーの設定
		$properties = $config->getProperty('properties');
		if ($properties !== null) {
			foreach ($properties as $key => $value) {
				if (substr($value, 0, 2) == '${' && substr($value, -1) == '}') {
					$value = $this->getBean(substr($value, 2, -1));
				}
				BeanUtils::setProperty($instance, $key, $value);
			}
		}

		// シングルトンで生成する場合はキャッシュ
		if ($singlton) {
			$this->instances[$name] = $instance;
		}

		$log->info('BeanManager : Instance bean '.$name);
		return $instance;
	}
}

/**
 * アクション
 *
 * @author Kenji Namba
 * @version 1.0
 * @since Barge 1.0 2006/08/12
 */
abstract class Action
{
	/**
	 * @var Logger
	 * @since 1.0 2006/09/25
	 */
	protected $log;

	/**
	 * @since 1.0 2006/09/25
	 */
	public function __construct()
	{
		$this->log = LogFactory::getLogger();
	}

	/**
	 * @param ActionContext $context
	 * @since 1.0 2006/09/25
	 */
	abstract public function execute(ActionContext $context);

	/**
	 * @return Logger
	 * @since 1.2
	 */
	public function getLog()
	{
		return $this->log;
	}
}

/**
 * デフォルトの認証ハンドラーの実装
 *
 * @see AuthHandler
 * @author Kenji Namba
 * @version 1.0
 * @since Barge 1.0 2006/08/12
 */
class DefaultAuthFilter implements ActionFilter
{
	/**
	 * @param ActionContext $context
	 * @param ActionFilterChain $chain
	 * @throws UnauthorizedException 認証エラー
	 */
	public function filter(ActionContext $context, ActionFilterChain $filterChain)
	{
		// 指定さされたページについて認証があるかどうか
		$user = $context->getUser();
		if (! $user->hasCredential($context->getModuleId(), $context->getActionId()) ) {
			$message = "認証されていない操作です。";
			throw new UnauthorizedException($message);
		}
		$chain->chain();
	}
}

/**
 * リクエストパラメータのためのフォーム
 * <p/>
 * Bargeでは型付きのパラメータをサポートしています。プロパティーの型は、プロパティーのイニシ
 * ャライザで設定した値により決定します。
 * <pre>
 * private $prop1 = "";		// 文字列型
 * private $prop2 = 0;		// 数値型
 * private $prop3 = false;	// Boolean
 * </pre>
 * フレームワークは、フォームへリクエストパラメータを割り付ける際にこれらの型を考慮し、可能な
 * 限り現在設定されている型を保持しようとします。詳細については、{@link ActionForm#populate}を参照してください。
 *
 * @see Bean
 * @author Kenji Namba
 * @version 1.0
 * @since Barge 1.0 2006/08/12
 */
abstract class ActionForm extends Bean
{
	/**
	 * @param Request $request
	 */
	public function reset(Request $request)
	{
		// Do nothing.
	}

	/**
	 * @param HashMap $map
	 */
	public function populate(HashMap $map)
	{
		$this->populateBean($this, $map, 3);
	}

	/**
	 * @param HashMap $map
	 */
	private function populateBean($bean, HashMap $map, $count)
	{
		if ($count <= 0) {
			return;
		}
$log=LogFactory::getLogger();
// TODO : 設定されている値をベースよりもアノテーションを強くする、アノテーションの場合は、強制的に変換を行う
// でなないと一度セッション上のフォームに書き込まれた値の型が強く影響してしまう
		static $trueExpressions = array('true', 'yes', 'on', 'TRUE', 'YES', 'ON', '1');
		foreach ($map as $name => $value) {
$log->debug($name);
			if ($bean instanceof HashMap) {
				$orgValue = $bean->get($name);
				$value = $this->convertType($value, $orgValue);
				$bean->put($name, $value);
			} else {
$log->debug('name1='.$name);
				$propertyDescriptor = BeanUtils::getPropertyDescriptor($bean, $name);
				if ($propertyDescriptor === null) {
					continue;
				}
$log->debug('name2='.$propertyDescriptor->getName());
				$propertyClass = null;
				$orgValue = null;

				// 書き込みメソッドにタイプヒンティングがある場合は、型を取得する
//				$writable = $propertyDescriptor->isWritable();
//				$readable = $propertyDescriptor->isReadable();
				$propertyClass = $propertyDescriptor->getClassType();

				/* @var $parameterClsss ReflectionClass */
//				$writeMethod = $propertyDescriptor->getWriteMethod();
//				$propertyClass = $propertyDescriptor->getClassType();
//				if ($writeMethod !== null) {
//					$parameters = $writeMethod->getParameters();
//					$propertyClass = $parameters[0]->getClass();
//					$writable = true;
//				}

				// 読み取りメソッドを取得し、値が取得できる場合は型を使用する
				if ($propertyDescriptor->isReadable()) {
					$orgValue = $propertyDescriptor->getReader()->read($bean);
					if ($propertyClass === null) {
						if (is_object($orgValue)) {
							$propertyClass = get_class($orgValue);
						}
					}
				}

				// 読み込み可能でパラメータクラスが確定している場合
	//if ($propertyClass !== null)
	//$log->debug('parameterClass='.$propertyClass->getName());
				if ($propertyDescriptor->isReadable() && $propertyClass !== null) {
					if ($orgValue === null) {		// 現在の値がない
						$reflectionClass = new ReflectionClass($propertyClass);
						$orgValue = $reflectionClass->newInstance();
						$propertyDescriptor->getWriter()->write($bean, $orgValue);
//						$writeMethod->invoke($bean, $orgValue);
					}
					// パラメータクラスと現在の値の型が一致する
					if ($orgValue !== null && get_class($orgValue) === $propertyClass) {
						if (is_array($value)) {
							// 現在のインスタンスに設定する
							if ($orgValue instanceof ArrayList) {
								// list[0]=1
								// list[0][name]
								foreach ($value as $idx => $element) {
									if (is_numeric($idx)) {
										if ($orgValue->size() > $idx) {
											//$orgValue->set($idx, $element);
											if (is_array($element)) {
												self::populateBean($orgValue->get($idx), new HashMap($element), $count - 1);
											} else {
												$orgValue->set($idx, $element);
											}
										} else {
											while ($orgValue->size() < $idx + 1)
												$orgValue->add(null);
											$orgValue->set($idx, $element);
										}
									}
								}
							} else {
								self::populateBean($orgValue, new HashMap($value), $count - 1);
							}
						}
					}
				} else {
					$value = $this->convertType($value, $orgValue);
					$propertyDescriptor->getWriter()->write($bean, $value);
				}
			}
		}

	}

	/**
	 * @param mixed  $value 変換する値
	 * @param mixed $orgValue 基準となる値
	 * @return unknown
	 */
	private function convertType($value, $orgValue)
	{
		if ($orgValue === null) return $value;
		$valueType = gettype($orgValue);
	    if ($valueType === 'string') {
		} else if ($valueType === 'integer') {
			$value = intval($value);
		} else if ($valueType === 'boolean') {
			if (is_bool($value)) {
			} else if (is_numeric($value)) {
				$value = ($value >= 1);
			} else if (is_string($value)) {
				$value = (in_array($value, $trueExpressions));
			} else {
				$value = ($value ? true : false);
			}
		}

		return $value;
	}

}

/**
 * イベントハンドラとしてアクションを使用する拡張
 * <p/>
 * <code>Context</code>を引数とする任意のメソッドをイベントハンドラとして指定できま
 * す。どのイベントが実行されるかどうかは、リクエストパラメータにメソッド名が存在するかどうか
 * になります。実行が可能なパラメータは、#getMethodsを実装することで指定ができます。パラメー
 * タが存在しない場合は、#getDefaultKeyが返すメソッド名からデファルトハンドラを決定します。
 * 通常は（サブクラスによって変更されない場合）、<code>init</code>です。
 * <pre>
 * class SomeAction extends DispatchAction
 * {
 * 		public function init(ActionContext $context)
 * 		{
 *
 * 		}
 * }
 * </pre>
 *
 * @see Action
 * @author Kenji Namba
 * @version 1.0
 * @since Barge 1.0 2006/08/12
 */
abstract class DispatchAction extends Action
{
	/**
	 * コンストラクタ
	 * @since 1.0 2006/09/25
	 */
	public function __construct()
	{
		parent::__construct();
	}

	/**
	 * アクションを実行する
	 * @param ActionContext $context
	 * @see Action#execute
	 * @since 1.0 2008/09/25
	 */
	public function execute(ActionContext $context)
	{
		// 有効なメソッドの一覧を取得する
		$methods = $this->getMethods();

		$handler = null;
		foreach ($methods as $temp) {
			if ($context->getRequest()->getParameter($temp) !== null) {
				$handler = $temp;
				break;
			}
			if ($context->getRequest()->getQueryParams()->contains($temp)) {
				$handler = $temp;
				break;
			}
		}
		if (System::isEmpty($handler)) {
			$handler = $this->getDefaultMethod();
		}

		if ($handler === 'execute') {
			$message = "'execute'を実行することはできません。";
			throw new IllegalAccessException($message);
		}

		$class = new ReflectionClass($this);
		if ($class->hasMethod($handler)) {
			$this->log->info('  => Execute dispatch '.get_class($this).'#'.$handler);
			$class->getMethod($handler)->invokeArgs($this, Array($context));
		} else {
			$message = "ハンドラー '{$handler}' は定義されていません。";
			throw new IllegalAccessException($message);
		}
	}

	/**
	 *
	 * @return ArrayList|array 許可するメソッド名の一覧
	 * @since 1.0 2006/09/25
	 */
	abstract protected function getMethods();

	/**
	 * 規定のメソッド名を返します、デフォルトでは、'init'が使用されます。
	 * @return string
	 * @since 1.0 2006/09/25
	 */
	protected function getDefaultMethod()
	{
		return 'init';
	}
}

/**
 * @author Kenji Namba
 * @version 1.0
 * @since Barge 1.3 2009/04/07
 */
class FormFile
{
	const OK = UPLOAD_ERR_OK;
	const ERROR_PARTIAL = UPLOAD_ERR_PARTIAL;
	const ERROR_CANT_WRITE = UPLOAD_ERR_CANT_WRITE;
	const ERROR_EXTENSION = UPLOAD_ERR_EXTENSION;
	const ERROR_FORM_SIZE = UPLOAD_ERR_FORM_SIZE;
	const ERROR_INI_SIZE = UPLOAD_ERR_INI_SIZE;
	const ERROR_NO_FILE = UPLOAD_ERR_NO_FILE;
	const ERROR_NO_TMP_DIR = UPLOAD_ERR_NO_TMP_DIR;

	/**
	 * @var string
	 */
	private $contentType;

	/**
	 * @var int
	 */
	private $fileSize = 0;

	/**
	 * @var string
	 */
	private $fileName;

	/**
	 * @var string
	 */
	private $tempFileName;

	/**
	 * @var int
	 */
	private $errorNo;

	/**
	 * @return int
	 */
    public function getFileSize()
    {
    	return $this->fileSize;
    }

	/**
	 * @param int $value
	 */
    public function setFileSize($value)
    {
    	$this->fileSize = $value;
    }

	/**
	 * application/octet-stream
	 * @return string
	 */
    public function getContentType()
    {
    	return $this->contentType;
    }

	/**
	 * @param string $value
	 */
    public function setContentType($value)
    {
    	$this->contentType = $value;
    }

	/**
	 * sample.jpeg
	 * @return string
	 */
    public function getFileName()
    {
    	return $this->fileName;
    }

	/**
	 * 一時ファイルのパスを返します
	 * @return string
	 */
    public function getTempFileName()
    {
    	return $this->tempFileName;
    }

	/**
	 * @param string $path
	 */
    public function setTempFileName($path)
    {
    	$this->tempFileName = $path;
    }

	/**
	 * @param string $value
	 */
    public function setFileName($value)
    {
    	$this->fileName = $value;
    }

	/**
	 * @return int
	 */
    public function getErrorNo()
    {
    	return $this->errorNo;
    }

	/**
	 * @param int $value
	 */
    public function setErrorNo($value)
    {
    	$this->errorNo = $value;
    }

   	/**
     * @return InputStream
     */
    public function getInputStream()
    {
    	return new FileInputStream($this->tempFileName);
    }

    /**
     * @param string $dir
     */
    public function moveTo($dir)
    {
    	if (!is_uploaded_file($this->tempFileName)) {
    		// FIXME :
    	}
    	if (@move_uploaded_file($this->tempFileName, $dir) === false) {
    		// FIXME :
    	}
    }

    /**
     * Destroys all content for the uploaded file, including any underlying
     * data files.
     */
    public function destroy()
    {
    	// Do nothing.
    }
}


/**
 * 入力内容検証用ユティリティ
 *
 * @version 1.0
 * @author Kenji Namba
 * @since Barge 1.0 2008/05/27
 */
class Validator
{
//	const DEFAULT_MAIL_PATTERN = "/[0-9a-z\.\-_]+@[0-9a-z\-_]+(.[0-9a-z\-_]+)+/";
//	const DEFAULT_DATE_PATTERN = "/(\d{4})[\-/\.](\d{4})/";

	/**
	 * 文字列が入力されたものかどうかを検証します
	 *
	 * @param string $value
	 * @return bool
	 * @since 1.0 2008/05/27
	 */
	public static function isPresent($value)
	{
		return !($value == null || trim($value) == '');
	}

	/**
	 * 最大文字列幅内であるかどうかを検証します。
	 *
	 * @param string $value
	 * @param int $minwidth,
	 * @param int $maxwidth
	 */
	public static function isValidWidth($value, $minwidth = 0, $maxwidth = null)
	{
		// TODO :
	}

	/**
	 * 文字列の個数が有効かどうかを検証します
	 * @param string $value
	 * @param int|string $minlen
	 * @param int|string $maxlen
	 * @return bool
	 * @since 1.0 2008/05/27
	 */
	public static function isValidLength($value, $minlen = 0, $maxlen = null)
	{
		$len = ($value === null ? 0 : mb_strlen($value));
		return ($len >= $minlen && ($maxlen === null || $len <= $maxlen));
	}

//	/**
//	 * 文字列の長さまたはバイト数が指定のもの以下であるかどうかをテストします。
//	 *
//	 * @param string $value
//	 * @param int $length
//	 * @param int $bytebase
//	 * @return bool
//	 * @since 1.0 2008/05/27
//	 */
//	public static function isValidLengthRange($value, $minlength, $maxlength, $bytebase = false)
//	{
//		$minlength = (is_string($minlength) ? self::toBytes($minlength) : $minlength);
//		$maxlength = (is_string($maxlength) ? self::toBytes($maxlength) : $maxlength);
//		if ($bytebase) {
//			$charset = System::getProperty('Validator', 'default_charset');
//			$length = ($value == null ? 0 : mb_strlen($value, $charset));
//			return ($length >= $minlength && $length <= $maxlength);
//		} else {
//			$length = ($value == null ? 0 : strlen($value));
//			return ($length >= $minlength && $length <= $maxlength);
//		}
//	}

	//	/**
	//	 * 文字列が半角数字で構成されているかどうかをテストします。
	//	 *
	//	 * @param string $value
	//	 * @return bool
	//	 * @since 1.0 2008/05/27
	//	 */
	//	public static function isSingleBytes($value)
	//	{
	//		return (preg_match("/^[0-9a-zA-Z!\"#$%&'\(\)=~|`@\{\[+;*:\}\]+$<,>\.?\/_]+/", $value));
	//	}

	/**
	 * @param string $value
	 * @return bool
	 * @since 1.0 2008/05/27
	 */
	public static function isValidFormat($formatkey, $value)
	{
		$format = System::getProperty('Validator/format.'.$formatkey);
		return (preg_match($format, $value));
	}



	/**
	 * @param string $express
	 * @return int
	 */
	private static function toBytes($express)
	{
		$sign = substr($express, -1);
		if ($sign == 'K') {
			return intval(substr($express, 0, -1)) * 1024;
		} else if ($sign == 'M') {
			return intval(substr($express, 0, -1)) * 1024 * 1024;
		} else if ($sign == 'G') {
			return intval(substr($express, 0, -1)) * 1024 * 1024 * 1024;
		} else {
			$message = "不明なサイズ書式 '{$express}'";
			throw new IllegalArgumentException($message);
		}
	}

	//	/**
	//	 * @param int val
	//	 * @param int min
	//	 * @param int max
	//	 * @return boolean
	//	 * @since 1.0 2008/05/27
	//	 */
	//	public static function isLengthRange($val, $min, $max)
	//	{
	//		return (($val == null ? 0 : mb_strlen($val, 'UTF-8')) <= $max) && (($val == null ? 0 : mb_strlen($val, 'UTF-8')) >= $min);
	//	}
	//
	//	/**
	//	 * @param mixed val
	//	 * @param mixed min
	//	 * @param mixed max
	//	 * @return boolean
	//	 * @since 1.0 2008/05/27
	//	 */
	//	public static function isValueRange($val, $min, $max)
	//	{
	//		return (intval($val) >= $min && intval($val) <= $max);
	//	}

	/**
	 * @param string $value
	 * @return boolean
	 */
	public static function isMail($value)
	{
		$format = System::getProperty('Validator/format.mail');
		return (preg_match($format, $value));
	}

	/**
	 * @param string $value
	 * @return boolean
	 */
	public static function isTelno($value)
	{
		$format = System::getProperty('Validator/format.telno');
		return (preg_match($format, $value));
	}

	/**
	 * @param string $value
	 * @return bool
	 * @since PHP 5.3
	 */
	public static function __callStatic($name, $arguments)
	{
		if (substr($name, 0, 2) == 'is') {
			$function = lcfirst(substr($name, 0, 1)) .substr($name, 4);
			$format = System::getProperty('Validator/format.'.$function);
			if ($format === null) {
				$message = "設定ファイルにバリデータの設定が見つかりません。key='format.{$function}'で呼び出します。";
				throw new IllegalAccessException($message);
			}
			return (preg_match($format, $arguments[0]));
		} else {
			$message = "バリデータのオートロードは'is'で呼び出します。";
			throw new IllegalAccessException($message);
		}
	}

	/**
	 * @param string $value
	 * @return bool
	 * @since 1.0 2006/09/11
	 */
	public static function isDate($value)
	{
		$format = System::getProperty('Validator/format/date', self::DEFAULT_DATE_PATTERN);
		$matchs = Array();
		if (preg_match($format, $value, $matchs) > 0) {
			return (!checkdate(intval($matchs[2]), intval($matchs[3]), intval($matchs[1])));
		} else {
			return false;
		}
	}

	/**
	 * @param string $value
	 * @return bool
	 * @since 1.0 - 2006/09/11
	 */
	public static function isTimestamp($value)
	{
		$format = System::getProperty('Validator/format/timestamp');
		$matchs = Array();
		if (preg_match($format, $value, $matchs)) {
			return (!checkdate($matchs[2], $matchs[3], $matchs[1]) &&
				self::checkTime($matchs[4], $matchs[5], $matchs[6]));
		} else {
			return false;
		}
	}

	/**
	 *
	 * @param string $hour
	 * @param string $minute
	 * @param string $second
	 * @return bool
	 */
	private static function checkTime($hour, $minute, $second)
	{
		return (is_numeric($hour) && intval($hour) == 0 && intval($hour) <= 23 &&
			is_numeric($minute) && intval($minute) == 0 && intval($minute) <= 59 &&
			is_numeric($second) && intval($second) == 0 && intval($second) <= 59);
	}

	/**
	 * @param string $value
	 * @return bool
	 * @since 1.0 - 2006/09/11
	 */
	public static function isNumerics($value)
	{
		return (is_numeric($value));
	}

	/**
	 * @param string $value
	 * @return bool
	 * @since 1.0 - 2006/09/11
	 */
	public static function isAlphas($value)
	{
		return (preg_match("/^[a-zA-Z]+$/", $value) > 0);
	}

	/**
	 * 文字列がアルファベット（A～Z,a～z）および数字（0～9）で構成されているかを検証する
	 *
	 * @param string $value
	 * @return bool
	 * @since 1.0 - 2006/09/11
	 */
	public static function isAlphaNumerics($value)
	{
		return (preg_match("/^[a-zA-Z0-9]+$/", (string)$value) > 0);
	}

//	/**
//	 * 文字列がカタカナで構成されているかどうかを検証する
//	 * @param string $value
//	 * @return bool
//	 * @since 1.0 - 2006/09/11
//	 */
//	public static function isFullKatakana($value)
//	{
////		new UnicodeSet(UnicodeSet::FULL_ALPHA_LOWER);
////		MultiByte::isFullKatakana
////		return (preg_match("/^[a-zA-Z0-9]+$/", (string)$value) > 0);
//	}

	/**
	 * 値<code>$value</code>がリスト<code>$list</code>の中に存在するかチェックします。
	 *
	 * @param string $value
	 * @param array|ArrayList $list
	 * @return boolean
	 * @since 1.0 - 2006/09/11
	 */
	public static function isWithin($value, $list)
	{
		foreach ($list as $item) {
			if ($value === $item) {
				return true;
			}
		}
		return false;
	}

	//
	//	/**
	//	 * @param file_path
	//	 * @return boolean
	//	 * @since 1.0 - 2006/09/11
	//	 */
	//	public static function isRequiredFile($file_path)
	//	{
	//		return (is_uploaded_file($file_path) && file_exists($file_path));
	//	}
	//
	//	/**
	//	 * @param string file_path
	//	 * @return boolean
	//	 * @since 1.0 - 2006/09/11
	//	 */
	//	public static function isFile($file_path)
	//	{
	//		return (file_exists($file_path) && filesize($file_path) > 0);
	//	}
	//
	//	/**
	//	 * @param string file_path
	//	 * @return boolean
	//	 * @since 1.0 - 2006/09/11
	//	 */
	//	public static function isImageFile($file_path)
	//	{
	//		list ($org_width, $org_height, $image_type, $org_attr) = getimagesize($file_path);
	//		return ($image_type == 1 || $image_type == 2 || $image_type == 3);
	//	}

	/*
	 //default_length_charset = "shift_jis"
	 format.mail = "/^[\w\d_]+(\.[\w\d_]+)*@[\w\d_]+(\.[\w\d_]+)*$/"
	 isNumerics
	 isAlphas
	 isSingles
	 //isValiedLength
	 //isValiedBytes
	 isValiedLengthRange
	 isMail
	 isTelno
	 isDate
	 isWithin
	 */
}

/**
 * テンプレートエンジンのラッパー
 *
 * @version 1.0
 * @author Kenji Namba
 * @since Barge 1.0 - 2006/08/13
 */
abstract class Renderer
{
	/**
	 * @var object
	 */
	protected $engine;

	/**
	 * @var string -
	 * @since 1.0 - 2006/09/12
	 */
	protected $template;

	/**
	 * @var string
	 * @since 1.0 - 2006/09/12
	 */
	protected $basedir;

	/**
	 * @var bool
	 * @since 1.0 - 2006/09/12
	 */
	protected $buffering = true;

	/**
	 * @var ActionContext
	 */
	protected $context = null;

	/**
	 * @var string
	 */
	protected $contentType = "text/html";

	/**
	 * テンプレートパスを指定するコンストラクタ
	 *
	 * @param string $templatePath
	 * @param string $contentType MIMEタイプ
	 */
	public function __construct($templatePath = null, $contentType = "text/html")
	{
		$this->template = $templatePath;
		$this->contentType = $contentType;
	}

	/**
	 * @return string
	 * @since 1.0 2006/09/12
	 */
	public function getTemplate()
	{
		return $this->template;
	}

	/**
	 * テンプレートパスを設定する
	 *
	 * @param string $path
	 * @since 1.0 2006/09/12
	 */
	public function setTemplate($path)
	{
		$this->template = $path;
	}

	/**
	 * @return object
	 * @since 1.0 2006/09/11
	 */
	public function getEngine()
	{
		return $this->engine;
	}

	/**
	 * @param object $engine
	 * @since 1.0 2006/09/11
	 */
	public function setEngine($engine)
	{
		$this->engine = $engine;
	}

	/**
	 * @return string
	 * @since 1.0 2006/09/11
	 */
	public function getBaseDir()
	{
		return $this->basedir;
	}

	/**
	 * @param string $basedir
	 * @since 1.0 2006/09/11
	 */
	public function setBaseDir($basedir)
	{
		$this->basedir = $basedir;
	}

	/**
	 * @return bool
	 * @since 1.0 2006/10/15
	 */
	public function isBuffering()
	{
		return $this->buffering;
	}

	/**
	 * @param bool $buffering
	 * @since 1.0 2006/10/15
	 */
	public function setBuffering($buffering)
	{
		$this->buffering = $buffering;
	}

	/**
	 * @param ActionContext $context
	 * @return void
	 * @since 1.0 2006/10/15
	 */
	public function setContext(ActionContext $context)
	{
		$this->context = $context;
	}

    /**
     * @param string $contentType
     */
    public function setContentType($contentType)
    {
    	$this->contentType = $contentType;
    }

	/**
	 * @param string $key
	 * @param string $value
	 * @since 1.0 2006/09/12
	 */
	abstract public function setAttribute($key, $value);

	/**
	 * @return void
	 * @since 1.0 - 2006/09/12
	 */
	abstract public function render();

	/**
	 * @return string
	 * @since 1.0 2006/09/12
	 */
	abstract public function getBuffer();

}


/**
 * @see Bean
 * @author Kenji Namba
 * @version 1.0
 * @since Barge 1.0 2006/08/14
 */
class Message extends Bean
{
	/**
	 * @var string
	 * @since 1.0 2006/08/14
	 */
	protected $property;

	/**
	 * @var string メッセージ文字列
	 * @since 1.0 2006/08/14
	 */
	protected $message;

	/**
	 * <pre>
	 * new Message('password', 'errors.required', 'パスワード');
	 * </pre>
	 * @param string $field フィールド名
	 * @param mixed $messageKey メッセージキー
	 * @param mixed $arg0 メッセージ引数1
	 * @param mixed $arg1 メッセージ引数2
	 * @param ... メッセージ引数3以降
	 * @since 1.0 2008/01/14
	 */
	public function __construct($field, $messageKey /*,$arg0, $arg1 , ...*/)
	{
		$args = func_get_args();

		$message = System::getProperty('MessageResources/'.$messageKey);
		$this->message = String::format($message, array_splice($args, 2));
		$this->property = $field;
	}

	/**
	 * @return string
	 * @since 1.0 2006/08/14
	 */
	public function getProperty()
	{
		return $this->property;
	}

	/**
	 * @return string
	 * @since 1.0 2006/08/14
	 */
	public function getMessage()
	{
		return $this->message;
	}
}


/**
 * @author Kenji Namba
 * @see ArrayList
 * @version 1.0
 * @since Barge 1.0 2006/08/14
 */
class Messages extends ArrayList
{
}


///**
// *
// * @version 1.0
// * @author Kenji Namba
// * @since Barge 1.0 - 2006/09/11
// */
//class Comparator
//{
//	private $property;
//
//	/**
//	 * A constructor
//	 */
//	public function __construct($property)
//	{
//		$this->property = $property;
//	}
//
//	public function cmpare(HashMap $a, HashMap $b)
//	{
//    	if ($a->get($this->property) == $b->get($this->property)) return 0;
//    	return ($a->get($this->property) > $b->get($this->property)) ? -1 : 1;
//	}
//}

